package com.example.demo.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.example.demo.constant.ResponseMsg;
import com.example.demo.util.acitiviti.HistoryProcessInstanceDiagramCmd;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;


/**
 * avtiviti操作service
 *
 * @author zhangfc
 * @date 2022/1/13 10:29
 */
@Service
@Slf4j
public class ActivitiService {

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    HistoryService historyService;

    @Autowired
    ManagementService managementService;

    @Resource
    HttpServletResponse servletResponse;

    @Autowired
    TaskService taskService;


    /**
     * 流程部署
     *
     * @param jsonString {resourcename:文件名,processname:流程name}
     * @return ResponseMsg
     */
    public ResponseMsg processDeployment(String jsonString) {

        final JSONObject jsonObject = JSONUtil.parseObj(jsonString);

        //bpmn文件,不带后缀名(.bpmn)
        final String resourcename = Convert.toStr(jsonObject.get("resourcename"));

        //部署的name
        final String processname = Convert.toStr(jsonObject.get("processname"));

        if (StrUtil.isEmpty(resourcename) || StrUtil.isEmpty(processname)) {
            return ResponseMsg.error("参数异常!");
        }

        /*
        根据resourcename拼出bpmn和png路径
         */
        final String bpmnResourceName = "diagrams/" + resourcename + ".bpmn";
        final String pngResourceName = "diagrams/" + resourcename + ".png";

        final Deployment deploy = repositoryService.createDeployment()
                .name(processname)
                .addClasspathResource(bpmnResourceName)
                .addClasspathResource(pngResourceName)
                .deploy();

        log.info("流程部署成功,部署id:{},部署name:{}", deploy.getId(), processname);

        return ResponseMsg.ok();
    }

    /**
     * 删除流程部署
     *
     * @param deploymentId 部署id
     * @param status       是否级联删除  true是 false否
     * @return ResponseMsg
     */
    public ResponseMsg deleteDeployment(String deploymentId, boolean status) {

        repositoryService.deleteDeployment(deploymentId, status);

        log.info("删除流程部署成功,部署id:{}", deploymentId);

        return ResponseMsg.ok();
    }

    /**
     * 删除流程实例和历史流程
     *
     * @param processInstanceId 流程实例id
     * @param deleteReason      删除理由
     * @return ResponseMsg
     */
    public ResponseMsg delProceeInstanceAndHistory(String processInstanceId, String deleteReason) {
        //删除流程实例
        ProcessInstance instance =
                runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (BeanUtil.isNotEmpty(instance)) {
            runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
            log.info("删除 processInstance,id->{}", processInstanceId);
        }

        //删除历史的流程实例
        HistoricProcessInstance historicProcessInstance
                = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        if (BeanUtil.isNotEmpty(historicProcessInstance)) {
            historyService.deleteHistoricProcessInstance(processInstanceId);
            log.info("删除 历史流程实例hisprocessInstance,id->{}", processInstanceId);
        }
        return ResponseMsg.ok();
    }

    /**
     * 启动流程实例
     *
     * @param processDefinitionKey 流程定义key
     * @param bussnessKey          业务主键
     * @param jsonString           流程变量
     * @return ResponseMsg
     */
    public ResponseMsg startProcessInstance(String processDefinitionKey,
                                            String bussnessKey,
                                            String jsonString) {
        ProcessInstance processInstance = null;
        //判断有无流程变量
        if (StrUtil.isNotEmpty(jsonString)) {
            TypeReference<Map<String, Object>> typeReference = new TypeReference<Map<String, Object>>() {
            };
            Map<String, Object> variables = JSONUtil.toBean(jsonString, typeReference, false);
            processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, bussnessKey, variables);
        } else {
            //无流程变量
            processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, bussnessKey);
        }

        if (BeanUtil.isEmpty(processInstance)) {
            log.error("启动流程失败, processDefinitionKey -> {}", processDefinitionKey);
            return ResponseMsg.error();
        }

        String id = processInstance.getId();

        log.info("启动流程实例成功,processInstanceId:{},processDefinitionKey:{},businessKey:{},variables:{}"
                , id, processDefinitionKey, bussnessKey, jsonString);

        return ResponseMsg.ok("流程实例启动成功");
    }

    /**
     * 废弃
     * 删除流程实例
     *
     * @param jsonString 参数 {processInstanceId:流程实例id,deleteReason:删除理由}
     * @return ResponseMsg
     */
    public ResponseMsg deleteProcessInstance(String jsonString) {
        final JSONObject jsonObject = JSONUtil.parseObj(jsonString);

        //流程实例id
        final String processInstanceId = Convert.toStr(jsonObject.get("processInstanceId"));

        //删除理由
        final String deleteReason = Convert.toStr(jsonObject.get("deleteReason"));
        ProcessInstance processInstance =
                runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        //流程实例非空时可删除
        if (BeanUtil.isNotEmpty(processInstance)) {
            runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
            log.info("删除流程实例成功,processInstanceId:{},deleteReason:{}", processInstanceId, deleteReason);

            return ResponseMsg.ok("流程实例删除成功");
        }
        return ResponseMsg.error("未找到对应的流程实例,删除失败");
    }

    /**
     * 获取流程动态图
     *
     * @param processInstanceId 流程实例id
     * @param bussniessKey      业务主键
     */
    public void dynamicDiagram(String processInstanceId, String bussniessKey) throws IOException {
        //检验参数
        if (StrUtil.isAllEmpty(processInstanceId, bussniessKey)) {
            throw new RuntimeException("参数解析异常,processInstanceId 和 bussniessKey 同时为空");
        }

        //当流程实例id为空时根据业务主键key获取流程实例
        if (StrUtil.isEmpty(processInstanceId)) {
            processInstanceId = this.getProcessIdbyBussinessKey(bussniessKey);
        }

        Command<InputStream> cmd = new HistoryProcessInstanceDiagramCmd(processInstanceId);
        InputStream inputStream = managementService.executeCommand(cmd);

        servletResponse.setContentType("image/png");
        int len = 0;
        byte[] b = new byte[1024];

        while ((len = inputStream.read(b, 0, 1024)) != -1) {
            servletResponse.getOutputStream().write(b, 0, len);
        }

    }

    /**
     * 获取任务节点的负责人
     *
     * @param processInstanceId 流程实例id
     * @param taskId            任务id
     * @return 任务负责人
     */
    public String getAssigneeByTask(String processInstanceId, String taskId) {
        Task task = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .taskId(taskId)
                .singleResult();
        if (BeanUtil.isEmpty(task)) {
            throw new RuntimeException("未查询到任务负责人");
        }
        return task.getAssignee();
    }

    public String getAssignessByProcessInstanceId(String processInstanceId){
        final Task task = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        if (BeanUtil.isEmpty(task)){
            throw new RuntimeException("未查询到任务负责人");
        }
        return task.getAssignee();
    }

    /**
     * 获取当前任务节点的上一个节点的负责人
     *
     * @param processInstanceId 流程实例id
     * @param taskId            任务id
     * @return 上一个节点负责人
     */
    public String getBeforeAssignee(String processInstanceId, String taskId) {


        List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByTaskCreateTime()
                .asc()
                .list();

        int index = 0;
        for (HistoricTaskInstance historicTaskInstance : historicTaskInstances) {
            if (StrUtil.equals(historicTaskInstance.getId(), taskId)) {
                break;
            }
            index++;
        }

        //如果taskId是第一个任务节点,没有上一个节点,返回null
        if (0 == index) {
            return null;
        }

        final HistoricTaskInstance historicTaskInstance = historicTaskInstances.get(index - 1);
        return historicTaskInstance.getAssignee();
    }

    /**
     * 完成任务
     *
     * @param taskId 任务id
     * @param variables 流程变量
     */
    public ResponseMsg completeTask(String taskId,Map<String,Object> variables){
        //校验参数
        if (MapUtil.isEmpty(variables)){
            //无流程变量
            taskService.complete(taskId);
            return ResponseMsg.ok();
        }
        //有流程变量
        taskService.complete(taskId,variables);
        return ResponseMsg.ok();
    }

    /**
     * 根据流程变量id查询唯一任务
     * @param processInstanceId 流程变量id
     * @return task
     */
    public Task getTaskByprocessInstanceId(String processInstanceId){
        return taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .processDefinitionKey("MyProcess")
                .singleResult();
    }

    /**
     * 重新分配任务
     * @param taskId 任务主键
     * @param assignee 新的受理人
     */
    public void setAssignee(String taskId,String assignee){
       final Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();
       if (BeanUtil.isNotEmpty(task)){
           task.setAssignee(assignee);
           return;
       }
       throw new RuntimeException("未查询到任务");
    }

    /**
     * 根据业务主键key获取流程实例id
     *
     * @param bussniessKey 业务主键key
     * @return 流程实例id
     */
    private String getProcessIdbyBussinessKey(String bussniessKey) {
        //获取正在运行的流程实例
        Execution processInstance = runtimeService.createExecutionQuery()
                .processInstanceBusinessKey(bussniessKey)
                .processDefinitionKey("MyProcess")
                .singleResult();
        if (BeanUtil.isEmpty(processInstance)) {
            //如果正在运行的流程实例找不到就从历史中查询
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceBusinessKey(bussniessKey)
                    .processDefinitionKey("MyProcess")
                    .singleResult();
            return historicProcessInstance.getId();
        } else {
            return processInstance.getProcessInstanceId();
        }
    }

}
